package com.qf.listener;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.qf.config.DelayedConfig;
import com.qf.constant.CacheConstants;
import com.qf.constant.RabbitMQConstants;
import com.qf.feign.CacheFeign;
import com.qf.model.StandardReport;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.Map;

/**
 * 监听短信发送状态报告, 推送给第三方用户
 */
@Component
@Slf4j
public class PushSmsReportListener {

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private CacheFeign cacheFeign;
    @Autowired
    private RestTemplate restTemplate;

    //状态报告发送次数和时间: 1,2,3,4,5    立即推送,10s之后， 30s之后， 1min， 5min
    private Integer[] sendTTL = {0, 10000, 30000, 60000, 300000};

    /**
     * 第一次发送状态推送报告
     */
    @RabbitListener(queues = RabbitMQConstants.TOPIC_PUSH_SMS_REPORT)
    public void messageHandler(StandardReport standardReport, Channel channel, Message message) {

        try {
            //1. 获取当前客户clientId
            long clientID = standardReport.getClientID();
            //2. 到redis中获取当前用户对象
            Map clientMap = cacheFeign.hMGet(CacheConstants.CACHE_PREFIX_CLIENT + clientID);
            //3. 从当前用户对象中找到receivestatusurl推送给第三方用户的地址
            String receivestatusurl = String.valueOf(clientMap.get("receivestatusurl"));

            if (!StringUtils.isEmpty(receivestatusurl)) {
                //a. 设置用户接受状态报告的回调地址
                standardReport.setReceiveStatusUrl(receivestatusurl);
                //b. 设置推送次数, 第一次推送
                standardReport.setSendCount(1);
                //c. 推送状态报告
                standardReport.setState(1);
                //推送状态报告给用户
                sendReport(standardReport);
            } else {
                //不推送状态报告
                log.info("[接口模块 - 状态报告推送] 用户不需要推送状态报告!, 流程执行完毕! standardReport={}", objectMapper.writeValueAsString(standardReport));
            }
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            //e.printStackTrace();
        }

    }

    /**
     * 监听延时队列状态报告, 推送
     * 除了第一次推送状态报告, 第N次都走这里
     *
     * @param standardReport 状态报告对象
     */
    @RabbitListener(queues = DelayedConfig.DELAYED_QUEUE)
    public void delayedMessageHandler(StandardReport standardReport,Channel channel,Message message) {
        try {
            //1. 第二~五次
            int sendCount = standardReport.getSendCount();
            if (sendCount >= 5) {
                standardReport.setState(2);
                log.error("[接口模块 - 状态报告推送] 推送报告状态：5次全失败了! standardReport={}", objectMapper.writeValueAsString(standardReport));
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
            //接着重新发送推送报告
            standardReport.setSendCount(sendCount + 1);
            //发送报告给用户
            sendReport(standardReport);
        } catch (IOException e) {
            //e.printStackTrace();
        }
    }

    /**
     * 发送状态报告给客户
     */
    private void sendReport(StandardReport standardReport) {
        try {
            //1. 封装请求数据
            HttpEntity httpEntity = new HttpEntity(standardReport);
            //2. 发送请求
            restTemplate.postForObject(standardReport.getReceiveStatusUrl(), httpEntity, String.class);
        } catch (Exception e) {
            //e.printStackTrace();
            //4. 如果出现异常, 再次进入到rabbitmq的延时队列中, 记录日志, 发送失败
            log.error("[接口模块 - 状态报告推送] 推送状态报告给用户抛出了异常!{}", e.getMessage());
            sendDelayedMessage(standardReport);
        }
    }

    /**
     * 将状态报告发送到rabbitMq中的延时队列
     *
     * @param standardReport 状态报告对象
     */
    private void sendDelayedMessage(StandardReport standardReport) {
        log.info("[接口模块 - 状态报告推送], 将状态报告发送到rabbitmq的延时队列, standardReport={}", standardReport);
        //判断状态报告发送次数, 要小于等于4次
        if (standardReport.getSendCount() <= 4) {
            //发送状态报告到延时交换机, 并设置消息生存时间
            //第一个参数: 交换器名字, 第二参数: 路由键, 第三参数:发送内容, 第四参数: 设置消息在队列中的超时时间
            rabbitTemplate.convertAndSend(DelayedConfig.DELAYED_EXCHANGE, DelayedConfig.DELAYED_ROUTING_KEY, standardReport,
                    (message) -> {
                    //设置状态报告在队列中的延时时间
                    message.getMessageProperties().setDelay(sendTTL[standardReport.getSendCount()]);
                    return message;
            });
        }
    }


}
